import json
from urllib import parse as urlparse

import requests
from requests_toolbelt import MultipartEncoder

from common import read_cfg
from common.constant import Constant
from common.my_logging import logger


class Request(object):
    def __init__(self):
        self._content_type = Constant.CONTENT_TYPE_URL_ENCODED
        self._method = 'POST'
        self._app = read_cfg.get_host_section()
        self._header_request_stack = json.dumps(self._app.to_header_request_stack())
        self._body_request_stack = None
        self._headers = {}
        self._url = ''
        self._proxy_url = '/api/v1/proxy/call?url='
        self._data = {}
        self._chains = []
        self._filename = None
        # 正式环境
        self._authorization = "N7dzUpi3snfLJ4Ty.P3YXoZysaIqg6vmH.9f5e11c32c90f8c02a79ea2fbf1e70c4"
        # 开发容器
        # self._authorization = "rBz0N2XEnUky8a5C.BvHDEX3SdVJuhUr4.78c2f99c29b4a91c17144f3c98a2dec6"

    def call(self,
             url: str,
             data: dict = None,
             content_type: str = None,
             chains: list = None,
             filename: str = None,
             method: str = 'POST'):
        if not url:
            return "url is null"

        self._method = method

        self._parse_url(url, chains)

        if data:
            if not isinstance(data, dict):
                return 'data must be dict'
            self._data = data

        if content_type:
            self._content_type = content_type

        if filename:
            self._filename = filename
            self._content_type = Constant.CONTENT_TYPE_MULTIPART

        if chains:
            self._chains = chains

        self._parse_body()
        self._prepare_header()

        return self._parse_response(self._request())

    def _prepare_header(self):
        if not self._filename:
            self._headers['Content-Type'] = self._content_type
        self._headers['User-Agent'] = Constant.USER_AGENT
        self._headers['RequestStack'] = self._header_request_stack
        self._headers['Accept'] = 'application/json'
        self._headers['Authorization'] = self._authorization

    def _parse_body(self):
        if self._chains:
            self._data['requestStack'] = json.dumps(self._chains)

        if self._content_type == Constant.CONTENT_TYPE_URL_ENCODED:
            form_url_encode_dict = {}
            self._encode_dict_key('', self._data, form_url_encode_dict)
            self._data = urlparse.urlencode(form_url_encode_dict)
            return

        if self._content_type == Constant.CONTENT_TYPE_JSON:
            self._data = json.dumps(self._data)
            return

        if self._content_type == Constant.CONTENT_TYPE_MULTIPART:
            if self._data:
                form_data = {}
                if isinstance(self._data, dict):
                    for key, value in self._data.items():
                        form_data.setdefault(key, (None, value))
                elif isinstance(self._data, (tuple, list)):
                    for td in self._data:
                        if isinstance(self._data, (tuple, list)):
                            form_data.setdefault(td[0], (None, td[1]))

                self._data = form_data

    def _request(self):
        logger.info("request-> url：{}, data：{}, headers：{}".format(self._url, self._data, self._headers))
        if self._content_type == Constant.CONTENT_TYPE_MULTIPART:
            if not self._filename:
                return requests.request(method=self._method, url=self._url, headers=self._headers, files=self._data,
                                        verify=False)
            with open(self._filename, 'rb') as f:
                form_data = MultipartEncoder(fields={**self._data, 'file': (self._filename, f)})
                return requests.request(method=self._method, url=self._url, headers=self._headers, files=form_data,
                                        verify=False)
        return requests.request(method=self._method, url=self._url, headers=self._headers, data=self._data,
                                verify=False)

    def _parse_response(self, response) -> str:
        logger.info(
            'response-> status: {}, content: {}, text: {}, reason: {}'.format(response.status_code, response.content,
                                                                              response.text, response.reason))
        if response.status_code == 200:
            if self._method == Constant.METHOD_OPTIONS:
                return response.headers
            return response.text
        else:
            return '{{"state":{},"msg":"{}","data": ""}}'.format(response.status_code, response.reason)

    def _encode_dict_key(self, key, data, result_dict: dict):
        if not data or not isinstance(data, (list, tuple, dict)):
            if not key:
                return
            else:
                result_dict.setdefault(key, data)
                return

        if isinstance(data, (list, tuple)):
            iter_data = enumerate(data)
        else:
            iter_data = data.items()

        for idx, v in iter_data:
            if not key:
                new_key = '{}'.format(str(idx))
            else:
                new_key = '{}[{}]'.format(key, str(idx))

            if not isinstance(v, (str, int, float)):
                self._encode_dict_key(new_key, v, result_dict)
            else:
                result_dict.setdefault(new_key, v)

    def _parse_url(self, url, chains):
        if url.startswith('/'):
            target_url = url
        else:
            target_url = '/' + url

        if chains:
            target_url = self._proxy_url + target_url
        self._url = self._app.host + target_url
